package com.jbwz.web.security.config;

import com.jbwz.web.common.filter.LogRequestFilter;
import com.jbwz.web.security.config.properties.SecurityUrlProperties;
import com.jbwz.web.security.config.security.AccessDecisionManger;
import com.jbwz.web.security.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.ForwardLogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Import(SecurityUrlProperties.class)
@Configuration
public class SecurityWebConfig {
  @Autowired private UserService userService;
  @Autowired private PasswordEncoder passwordEncoder;
  @Autowired private AccessDecisionManger accessDecisionManger;
  final SecurityUrlProperties securityUrlProperties;

  public SecurityWebConfig(SecurityUrlProperties securityUrlProperties) {
    this.securityUrlProperties = securityUrlProperties;
  }

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.csrf()
        .disable()
        .anonymous()
        .disable()
        .addFilterBefore(new LogRequestFilter(), LogoutFilter.class)
        .authorizeHttpRequests(
            (authorize) ->
                authorize
                    .antMatchers(getNoneUrl())
                    .permitAll()
                    .antMatchers(getAuthUrlOnly())
                    .authenticated()
                    .anyRequest()
                    .access(accessDecisionManger))
        .logout(lout -> lout.logoutSuccessHandler(new ForwardLogoutSuccessHandler("/logout")))
        .formLogin(
            formLogin ->
                formLogin
                    .successForwardUrl("/login-success")
                    .failureForwardUrl("/login-error")
                    .permitAll())
        .exceptionHandling(
            sec -> {
              LoginUrlAuthenticationEntryPoint authenticationEntryPoint =
                  new LoginUrlAuthenticationEntryPoint("/login");
              authenticationEntryPoint.setUseForward(true);
              sec.authenticationEntryPoint(authenticationEntryPoint);
            })
        .sessionManagement(
            session ->
                session
                    .invalidSessionStrategy(
                        ((request, response) ->
                            request
                                .getRequestDispatcher("/login-expired")
                                .forward(request, response)))
                    .maximumSessions(1));
    return http.build();
  }

  private String[] getNoneUrl() {
    List<String> noneUrl =
        securityUrlProperties.getPermitAll().stream()
            .peek(StringUtils::trimWhitespace)
            .filter(StringUtils::hasLength)
            .toList();
    List<String> paths = new ArrayList<>(noneUrl);
    //    paths.add("/company/reg");
    paths.add("/logout");
    paths.add("/login-expired");
    return paths.toArray(new String[0]);
  }

  private String[] getAuthUrlOnly() {
    List<String> authUrl =
        securityUrlProperties.getAuthOnly().stream()
            .peek(StringUtils::trimWhitespace)
            .filter(StringUtils::hasLength)
            .toList();
    List<String> paths = new ArrayList<>(authUrl);
    paths.add("/user/current-user");
    paths.add("/user/pkeys");
    return paths.toArray(new String[0]);
  }

  @Bean
  public UserDetailsService userDetailsService() {
    return userService;
  }

  @Bean
  public AuthenticationProvider authenticationProvider() {
    DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
    daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
    daoAuthenticationProvider.setUserDetailsService(userService);
    return daoAuthenticationProvider;
  }
}
